package com.jmp.api.service.impl;

import com.gexin.fastjson.JSON;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.jmp.api.dto.MessagePushDto;
import com.jmp.api.service.MessageService;
import com.jmp.base.LabelApi;
import com.jmp.base.MessageApi;
import com.jmp.base.dto.MessagePushClientEnum;
import com.jmp.base.dto.SecurityBaseClientDto;
import com.jmp.push.PushClient;
import com.jmp.push.PushClientRequest;
import com.jmp.push.PushListRequest;
import lombok.extern.slf4j.Slf4j;
import one.stand.advice.ResultEnum;
import one.stand.dto.ConvertUtils;
import one.stand.enums.LoginEnum;
import one.stand.mapper.CompanyCustomerLableMapperExt;
import one.stand.mapper.CompanyPlatformMapperExt;
import one.stand.mapper.UserMapperExt;
import one.stand.mapper.UserPushMapperExt;
import one.stand.model.BusinessCard;
import one.stand.model.CompanyPlatform;
import one.stand.model.UserPush;
import one.stand.service.BusinessCardBaseService;
import one.stand.service.RecordPublicService;
import one.stand.service.SecurityBaseService;
import one.stand.util.AssertUtil;
import one.stand.util.OneListUtil;
import one.stand.util.SqlUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * @author cjr
 */
@Service
@Slf4j
public class MessageServiceImpl implements MessageService {

    @Autowired
    private RecordPublicService recordPublicService;
    @Autowired
    private BusinessCardBaseService businessCardBaseService;
    @Autowired
    private SecurityBaseService securityBaseService;

    @Autowired
    private PushClient pushClient;
    @Autowired
    private CompanyCustomerLableMapperExt companyCustomerLableMapperExt;
    @Autowired
    private UserMapperExt userMapperExt;
    @Autowired
    private UserPushMapperExt userPushMapperExt;
    @Autowired
    private CompanyPlatformMapperExt companyPlatformMapperExt;

    @Override
    @Async("asyncServiceExecutor")
    public void savePush(Integer userId, String pushAppId, String pushId) {

        UserPush userPush = userPushMapperExt.select(userId, pushAppId);
        if (Objects.isNull(userPush)) {
            userPush = new UserPush();
            userPush.setUserId(userId);
            userPush.setPushAppId(pushAppId);
            userPush.setPushCid(pushId);
            userPushMapperExt.insertSelective(userPush);
        } else {
            UserPush userPushUpdate = new UserPush();
            userPushUpdate.setId(userPush.getId());
            userPushUpdate.setPushCid(pushId);
            userPushUpdate.setUpdateTime(new Date());
            userPushMapperExt.updateByPrimaryKeySelective(userPushUpdate);
        }
    }

    @Override
    @Async("asyncServiceExecutor")
    public void pushAll(MessagePushDto messageDto) {
        AssertUtil.nonNull(messageDto.getType(), ResultEnum.PARAM_CHECK);
        log.info("本次推送为全部推送");
        List<String> appIds = pushClient.getAppIds();
        for (String appId : appIds) {
            PushClientRequest pushClientRequest = PushClientRequest.builder()
                    .appId(appId)
                    .title(messageDto.getPushTitle())
                    .content(messageDto.getPushContent())
                    .payloadContent(messageDto.getPushPayload(appId))
                    .build();
            pushClient.toAll(pushClientRequest);
        }
    }

    @Override
    @Async("asyncServiceExecutor")
    public void push(MessagePushDto messageDto) {
        AssertUtil.nonNull(messageDto.getType(), ResultEnum.PARAM_CHECK);
        if (Objects.isNull(messageDto.getToType())) {
            messageDto.setToType(MessageApi.TYPE_USER);
        }
        log.info("push: {}", JSON.toJSONString(messageDto));

        Map<Integer, List<Integer>> companyUserIdMap = getCompanyUserIdMap(messageDto);
        if (companyUserIdMap.size() <= 0) {
            log.warn("push toUserIds is null");
            return;
        }

//        Map<String, List<Integer>> pushUserIdMap = Maps.newHashMap();
//        String pushAppId;
//        for (Map.Entry<Integer, List<Integer>> companyUserId : companyUserIdMap.entrySet()) {
//            pushAppId = getPushAppId(companyUserId.getKey());
//            if (pushUserIdMap.containsKey(pushAppId)) {
//                pushUserIdMap.get(pushAppId).addAll(companyUserId.getValue());
//            } else {
//                pushUserIdMap.put(pushAppId, companyUserId.getValue());
//            }
//        }

//        List<UserPush> pushTargets = Lists.newArrayList();
//        for (Map.Entry<String, List<Integer>> pushUserId : pushUserIdMap.entrySet()) {
//            pushTargets.addAll(pushByPushClient(pushUserId.getValue(), pushUserId.getKey()));
//        }

        List<Integer> userIds = Lists.newArrayList();
        for (List<Integer> list : companyUserIdMap.values()) {
            userIds.addAll(list);
        }
        List<UserPush> pushTargets = pushByPushClientByLast(userIds, messageDto.getPushClient());

        if (OneListUtil.isEmpty(pushTargets)) {
            log.warn("push targets is null");
            return;
        }
        push(pushTargets, messageDto);
    }

    /**
     * 无指定端时，根据平台获取，优先JxsApp, 按默认
     */
    private String getPushAppId(Integer companyId) {
        if (Objects.isNull(companyId)) return null;
        CompanyPlatform companyPlatform = companyPlatformMapperExt.selectPlatformByCompanyId(companyId);
        String pushAppId = null;
        if (Objects.nonNull(companyPlatform)) {
            pushAppId = pushClient.getAppId(companyPlatform.getCompanyKey() + LoginEnum.JXS_APP.getPushFlag());
            if (StringUtils.isBlank(pushAppId)) {
                pushAppId = pushClient.getAppId(companyPlatform.getCompanyKey());
            }
        } else {
            pushAppId = pushClient.getAppId();
        }
        return pushAppId;
    }

    /**
     * 根据最后登录端，查询推送ID
     * 如果存在指定端{@link MessagePushClientEnum}，则按指定端查询推送ID
     */
    private List<UserPush> pushByPushClientByLast(List<Integer> pushUserIdList, MessagePushClientEnum pushClient) {
        String pushAppId = null;
        if (Objects.nonNull(pushClient)) {
            SecurityBaseClientDto clientDto = securityBaseService.getClient(pushClient.getCode());
            if (Objects.nonNull(clientDto)) {
                pushAppId = clientDto.getPushAppId();
            }
        }
        return userPushMapperExt.selectLast(SqlUtil.in(pushUserIdList), pushAppId);
    }

    /**
     * 根据AppId{@code clientAppIds}优先级，查询用户推送ID
     */
    private List<UserPush> pushByPushClient(List<Integer> pushUserIdList, String defaultAppId) {
        List<String> clientAppIds = Lists.newArrayList(defaultAppId);
        clientAppIds.addAll(pushClient.getAppIds());
        List<UserPush> pushTargets = Lists.newArrayList();
        for (int index = clientAppIds.size() - 1; index >= 0; index--) {
            if (OneListUtil.isEmpty(pushUserIdList)) break;
            List<UserPush> pushTargetsPart = userPushMapperExt.selectList(SqlUtil.in(pushUserIdList), clientAppIds.get(index));
            pushTargets.addAll(pushTargetsPart);
            for (UserPush userPush : pushTargetsPart) {
                int i = pushUserIdList.indexOf(userPush.getUserId());
                if (i >= 0) {
                    pushUserIdList.remove(i);
                }
            }
        }
        return pushTargets;
    }

    private Map<Integer, List<Integer>> getCompanyUserIdMap(MessagePushDto messageDto) {
        Map<Integer, List<Integer>> companyUserIdMap = Maps.newHashMap();
        if (MessageApi.TYPE_USER == messageDto.getToType()) {
            BusinessCard businessCard = businessCardBaseService.getByUserId(messageDto.getToId());
            companyUserIdMap.put(businessCard.getCompanyId(), Lists.newArrayList(messageDto.getToId()));
        } else if (MessageApi.TYPE_LABELS_COMPANY == messageDto.getToType()) {
            String labels = SqlUtil.in(messageDto.getToIds());
            List<Integer> companyIds = companyCustomerLableMapperExt.selectTypeId(labels, LabelApi.LABEL_TYPE_COMPANY);
            if (OneListUtil.isNotEmpty(companyIds)) {
                for (Integer companyId : companyIds) {
                    List<Integer> userIds = userMapperExt.selectByCompanyIds(companyId.toString());
                    companyUserIdMap.put(companyId, userIds);
                }
            }
        } else if (MessageApi.TYPE_COMPANY == messageDto.getToType()) {
            List<Integer> userIds = userMapperExt.selectByCompanyIds(messageDto.getToId().toString());
            companyUserIdMap.put(messageDto.getToId(), userIds);
        } else if (MessageApi.TYPE_COLLECTION == messageDto.getToType()) {
            List<Integer> userIds = recordPublicService.getCollection(messageDto.getToChildType(), messageDto.getToId());
            companyUserIdMap.put(null, userIds);
        } else {
            AssertUtil.fail("type have no handler");
        }
        return companyUserIdMap;
    }

    private void push(List<UserPush> pushTargets, MessagePushDto messageDto) {

        boolean sameTitleAndContent = messageDto.getTypeEnum().isSameTitleAndContent();// 推送标题和内容一致时为true
        if (pushTargets.size() == 1) { // single
            log.info("push single");
            UserPush userPush = pushTargets.get(0);
            PushClientRequest pushClientRequest = PushClientRequest.builder()
                    .appId(userPush.getPushAppId())
                    .cid(userPush.getPushCid())
                    .title(messageDto.getPushTitle())
                    .content(messageDto.getPushContent())
                    .payloadContent(messageDto.getPushPayload(userPush.getPushAppId()))
                    .build();
            pushClient.toOne(pushClientRequest);
            return;
        } else if (pushTargets.size() >= 1 && sameTitleAndContent) {// list and content is one
            log.info("push to list");
            Map<String, List<UserPush>> pushMap = pushTargets.stream().collect(Collectors.groupingBy(UserPush::getPushAppId));
            for (Map.Entry<String, List<UserPush>> push : pushMap.entrySet()) {
                PushListRequest pushListRequest = PushListRequest.builder()
                        .cids(ConvertUtils.list(push.getValue(), pushTarget -> PushListRequest.PushTarget.builder()
                                .appId(pushTarget.getPushAppId())
                                .cid(pushTarget.getPushCid())
                                .build()))
                        .title(messageDto.getPushTitle())
                        .content(messageDto.getPushContent())
                        .payloadContent(messageDto.getPushPayload(push.getKey()))
                        .build();
                pushClient.toList(pushListRequest);
            }
            return;
        }

        if (pushTargets.size() > 0 && !sameTitleAndContent) {
            log.info("push single batch");
            List<PushClientRequest> pushClientRequests = ConvertUtils.list(pushTargets, pushTarget -> PushClientRequest.builder()
                    .appId(pushTarget.getPushAppId())
                    .cid(pushTarget.getPushCid())
                    .title(messageDto.getPushTitle())
                    .content(messageDto.getPushContent())
                    .payloadContent(messageDto.getPushPayload(pushTarget.getPushAppId()))
                    .build());
            pushClient.toOneBatch(pushClientRequests);
        }

        log.error("no handler, no push");
    }
}
